"""
认证管理器

负责用户认证、会话管理、审计日志等功能
"""

import sys
import os
from typing import Dict, Optional, Any
from datetime import datetime, timedelta

# 添加路径
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
operating_system_dir = os.path.join(parent_dir, "OperatingSystem")
if operating_system_dir not in sys.path:
    sys.path.insert(0, operating_system_dir)

from privilege_types import UserInfo, PermissionResult
from security_utils import SecurityUtils
from user_manager import UserManager
from permission_manager import PermissionManager
from system_schema import SYSTEM_DATABASE_NAME


class AuthSession:
    """认证会话"""
    
    def __init__(self, session_id: str, user_info: UserInfo, 
                 created_time: datetime = None, client_host: str = None):
        self.session_id = session_id
        self.user_info = user_info
        self.created_time = created_time or datetime.now()
        self.last_activity = datetime.now()
        self.client_host = client_host
        self.is_active = True
        self.current_database = None
        self.privileges_cache = {}  # 会话级权限缓存
    
    def update_activity(self):
        """更新最后活动时间"""
        self.last_activity = datetime.now()
    
    def is_expired(self, timeout_minutes: int = 30) -> bool:
        """检查会话是否过期"""
        if not self.is_active:
            return True
        
        timeout_delta = timedelta(minutes=timeout_minutes)
        return datetime.now() - self.last_activity > timeout_delta
    
    def get_session_info(self) -> Dict[str, Any]:
        """获取会话信息"""
        return {
            "session_id": self.session_id,
            "username": self.user_info.username,
            "host": self.user_info.host,
            "client_host": self.client_host,
            "created_time": self.created_time.isoformat(),
            "last_activity": self.last_activity.isoformat(),
            "current_database": self.current_database,
            "is_active": self.is_active
        }


class AuthManager:
    """认证管理器"""
    
    def __init__(self, database_manager):
        """
        初始化认证管理器
        
        Args:
            database_manager: 数据库管理器实例
        """
        self.database_manager = database_manager
        self.system_db = None
        
        # 组件管理器
        self.user_manager = UserManager(database_manager)
        self.permission_manager = PermissionManager(database_manager)
        
        # 会话管理
        self.active_sessions: Dict[str, AuthSession] = {}
        self.session_timeout_minutes = 30
        
        # 审计日志配置
        self.enable_audit_log = True
        self.max_login_attempts = 5
        self.lockout_duration_minutes = 15
        self.failed_attempts: Dict[str, list] = {}  # {user_key: [attempt_times]}
        
        # 获取系统数据库实例
        self._initialize_system_database()
    
    def _initialize_system_database(self):
        """初始化系统数据库"""
        try:
            self.system_db = self.database_manager.get_database(SYSTEM_DATABASE_NAME)
            if not self.system_db:
                raise RuntimeError(f"System database '{SYSTEM_DATABASE_NAME}' not found")
        except Exception as e:
            raise RuntimeError(f"Failed to initialize system database: {e}")
    
    def authenticate(self, username: str, password: str, client_host: str = None) -> Optional[str]:
        """
        用户认证
        
        Args:
            username: 用户名
            password: 密码
            client_host: 客户端主机
            
        Returns:
            Optional[str]: 认证成功返回session_id，否则返回None
        """
        try:
            # 清理输入
            username = SecurityUtils.sanitize_input(username, 64)
            client_host = SecurityUtils.sanitize_input(client_host or 'unknown', 64)
            
            # 检查用户是否被锁定
            if self._is_user_locked(username):
                self._log_audit("LOGIN_FAILED", username, client_host, "User locked due to failed attempts")
                return None
            
            # 验证用户身份
            user_info = self.user_manager.authenticate_user(username, password, client_host)
            
            if user_info:
                # 认证成功
                session_id = self._create_session(user_info, client_host)
                
                # 清除失败尝试记录
                user_key = f"{username}@{user_info.host}"
                if user_key in self.failed_attempts:
                    del self.failed_attempts[user_key]
                
                # 记录审计日志
                self._log_audit("LOGIN_SUCCESS", username, client_host, f"Session created: {session_id}")
                
                return session_id
            else:
                # 认证失败
                self._record_failed_attempt(username)
                self._log_audit("LOGIN_FAILED", username, client_host, "Invalid credentials")
                return None
                
        except Exception as e:
            self._log_audit("LOGIN_ERROR", username, client_host, f"Authentication error: {e}")
            return None
    
    def logout(self, session_id: str) -> bool:
        """
        用户注销
        
        Args:
            session_id: 会话ID
            
        Returns:
            bool: 注销是否成功
        """
        try:
            if session_id not in self.active_sessions:
                return False
            
            session = self.active_sessions[session_id]
            session.is_active = False
            
            # 记录审计日志
            self._log_audit("LOGOUT", session.user_info.username, session.client_host, f"Session ended: {session_id}")
            
            # 从活动会话中移除
            del self.active_sessions[session_id]
            
            return True
            
        except Exception as e:
            print(f"Error during logout for session {session_id}: {e}")
            return False
    
    def validate_session(self, session_id: str) -> Optional[AuthSession]:
        """
        验证会话
        
        Args:
            session_id: 会话ID
            
        Returns:
            Optional[AuthSession]: 有效会话返回会话对象，否则返回None
        """
        try:
            if session_id not in self.active_sessions:
                return None
            
            session = self.active_sessions[session_id]
            
            # 检查会话是否过期
            if session.is_expired(self.session_timeout_minutes):
                self._expire_session(session_id)
                return None
            
            # 更新活动时间
            session.update_activity()
            
            return session
            
        except Exception as e:
            print(f"Error validating session {session_id}: {e}")
            return None
    
    def check_permission(self, session_id: str, operation: str, 
                        database_name: str = None, table_name: str = None) -> PermissionResult:
        """
        检查会话权限
        
        Args:
            session_id: 会话ID
            operation: 操作类型
            database_name: 数据库名称
            table_name: 表名称
            
        Returns:
            PermissionResult: 权限检查结果
        """
        try:
            # 验证会话
            session = self.validate_session(session_id)
            if not session:
                return PermissionResult(
                    allowed=False,
                    reason="Invalid or expired session",
                    audit_info={"session_id": session_id}
                )
            
            # 检查操作权限
            result = self.permission_manager.check_operation_privilege(
                session.user_info, operation, database_name, table_name
            )
            
            # 记录权限检查审计
            if not result.allowed:
                self._log_audit(
                    "ACCESS_DENIED", 
                    session.user_info.username, 
                    session.client_host,
                    f"Operation: {operation}, Database: {database_name}, Table: {table_name}, Reason: {result.reason}"
                )
            
            return result
            
        except Exception as e:
            return PermissionResult(
                allowed=False,
                reason=f"Permission check error: {e}",
                audit_info={"error": str(e)}
            )
    
    def get_user_accessible_databases(self, session_id: str, all_databases: list) -> list:
        """
        获取用户可访问的数据库列表
        
        Args:
            session_id: 会话ID
            all_databases: 所有数据库列表
            
        Returns:
            list: 用户可访问的数据库列表
        """
        try:
            session = self.validate_session(session_id)
            if not session:
                return []
            
            return self.permission_manager.get_accessible_databases(session.user_info, all_databases)
            
        except Exception as e:
            print(f"Error getting accessible databases for session {session_id}: {e}")
            return []
    
    def create_user(self, session_id: str, username: str, password: str, host: str = '%') -> bool:
        """
        创建用户（需要CREATE_USER权限）
        
        Args:
            session_id: 执行者会话ID
            username: 新用户名
            password: 新用户密码
            host: 新用户主机
            
        Returns:
            bool: 创建是否成功
        """
        try:
            # 检查权限
            perm_result = self.check_permission(session_id, "create_user")
            if not perm_result.allowed:
                return False
            
            session = self.active_sessions[session_id]
            
            # 创建用户
            success = self.user_manager.create_user(
                username, password, host, created_by=session.user_info.username
            )
            
            if success:
                self._log_audit("CREATE_USER", session.user_info.username, session.client_host, 
                              f"Created user: {username}@{host}")
            
            return success
            
        except Exception as e:
            print(f"Error creating user {username}@{host}: {e}")
            return False
    
    def drop_user(self, session_id: str, username: str, host: str = '%') -> bool:
        """
        删除用户（需要DROP_USER权限）
        
        Args:
            session_id: 执行者会话ID
            username: 用户名
            host: 主机
            
        Returns:
            bool: 删除是否成功
        """
        try:
            # 检查权限
            perm_result = self.check_permission(session_id, "drop_user")
            if not perm_result.allowed:
                return False
            
            session = self.active_sessions[session_id]
            
            # 删除用户
            success = self.user_manager.drop_user(username, host)
            
            if success:
                self._log_audit("DROP_USER", session.user_info.username, session.client_host,
                              f"Dropped user: {username}@{host}")
                
                # 终止该用户的所有会话
                self._terminate_user_sessions(username, host)
            
            return success
            
        except Exception as e:
            print(f"Error dropping user {username}@{host}: {e}")
            return False
    
    def grant_privilege(self, session_id: str, privilege_type: str, resource_type: str,
                       resource_name: str, target_username: str, target_host: str = '%') -> bool:
        """
        授予权限（需要GRANT权限）
        
        Args:
            session_id: 执行者会话ID
            privilege_type: 权限类型
            resource_type: 资源类型
            resource_name: 资源名称
            target_username: 目标用户名
            target_host: 目标主机
            
        Returns:
            bool: 授权是否成功
        """
        try:
            # 检查权限（简化实现，应该检查GRANT权限）
            session = self.validate_session(session_id)
            if not session:
                return False
            
            # 这里应该检查执行者是否有相应的GRANT权限
            # 简化实现：只有root用户可以授权
            if session.user_info.username != 'root':
                return False
            
            from privilege_types import PrivilegeType, ResourceType
            
            # 转换字符串到枚举
            priv_type = PrivilegeType(privilege_type)
            res_type = ResourceType(resource_type)
            
            success = self.permission_manager.grant_privilege(
                priv_type, res_type, resource_name, target_username, target_host,
                granted_by=session.user_info.username
            )
            
            if success:
                self._log_audit("GRANT_PRIVILEGE", session.user_info.username, session.client_host,
                              f"Granted {privilege_type} on {resource_type} {resource_name} to {target_username}@{target_host}")
            
            return success
            
        except Exception as e:
            print(f"Error granting privilege: {e}")
            return False
    
    def revoke_privilege(self, session_id: str, privilege_type: str, resource_type: str,
                        resource_name: str, target_username: str, target_host: str = '%') -> bool:
        """
        撤销权限（需要GRANT权限）
        
        Args:
            session_id: 执行者会话ID
            privilege_type: 权限类型
            resource_type: 资源类型
            resource_name: 资源名称
            target_username: 目标用户名
            target_host: 目标主机
            
        Returns:
            bool: 撤销是否成功
        """
        try:
            # 检查权限（简化实现，应该检查GRANT权限）
            session = self.validate_session(session_id)
            if not session:
                return False
            
            # 这里应该检查执行者是否有相应的GRANT权限
            # 简化实现：只有root用户可以撤销权限
            if session.user_info.username != 'root':
                return False
            
            from privilege_types import PrivilegeType, ResourceType
            
            # 转换字符串到枚举
            priv_type = PrivilegeType(privilege_type)
            res_type = ResourceType(resource_type)
            
            success = self.permission_manager.revoke_privilege(
                priv_type, res_type, resource_name, target_username, target_host
            )
            
            if success:
                self._log_audit("REVOKE_PRIVILEGE", session.user_info.username, session.client_host,
                              f"Revoked {privilege_type} on {resource_type} {resource_name} from {target_username}@{target_host}")
            
            return success
            
        except Exception as e:
            print(f"Error revoking privilege: {e}")
            return False
    
    def _create_session(self, user_info: UserInfo, client_host: str) -> str:
        """创建会话"""
        session_id = SecurityUtils.generate_session_token()
        
        session = AuthSession(
            session_id=session_id,
            user_info=user_info,
            client_host=client_host
        )
        
        self.active_sessions[session_id] = session
        
        return session_id
    
    def _expire_session(self, session_id: str):
        """使会话过期"""
        if session_id in self.active_sessions:
            session = self.active_sessions[session_id]
            session.is_active = False
            
            self._log_audit("SESSION_EXPIRED", session.user_info.username, session.client_host,
                          f"Session expired: {session_id}")
            
            del self.active_sessions[session_id]
    
    def _is_user_locked(self, username: str) -> bool:
        """检查用户是否被锁定"""
        user_key = f"{username}@%"  # 简化实现，使用通配符主机
        
        if user_key not in self.failed_attempts:
            return False
        
        attempts = self.failed_attempts[user_key]
        
        # 清理过期的失败尝试
        cutoff_time = datetime.now() - timedelta(minutes=self.lockout_duration_minutes)
        attempts = [t for t in attempts if t > cutoff_time]
        self.failed_attempts[user_key] = attempts
        
        return len(attempts) >= self.max_login_attempts
    
    def _record_failed_attempt(self, username: str):
        """记录失败的登录尝试"""
        user_key = f"{username}@%"  # 简化实现，使用通配符主机
        
        if user_key not in self.failed_attempts:
            self.failed_attempts[user_key] = []
        
        self.failed_attempts[user_key].append(datetime.now())
        
        # 只保留最近的尝试记录
        cutoff_time = datetime.now() - timedelta(minutes=self.lockout_duration_minutes)
        self.failed_attempts[user_key] = [
            t for t in self.failed_attempts[user_key] if t > cutoff_time
        ]
    
    def _terminate_user_sessions(self, username: str, host: str):
        """终止指定用户的所有会话"""
        sessions_to_remove = []
        
        for session_id, session in self.active_sessions.items():
            if (session.user_info.username == username and 
                (session.user_info.host == host or host == '%')):
                sessions_to_remove.append(session_id)
        
        for session_id in sessions_to_remove:
            self.logout(session_id)
    
    def _log_audit(self, operation_type: str, username: str = None, 
                   client_host: str = None, details: str = None):
        """记录审计日志"""
        if not self.enable_audit_log:
            return
        
        try:
            audit_record = {
                "username": username,
                "host": client_host,
                "operation_type": operation_type,
                "resource_type": None,
                "resource_name": None,
                "operation_time": datetime.now(),
                "status": "SUCCESS",
                "details": details
            }
            
            # 生成日志ID（简化实现）
            audit_record["log_id"] = hash(f"{datetime.now().isoformat()}{username}{operation_type}") % 1000000
            
            self.system_db.insert_record("audit_log", audit_record)
            
        except Exception as e:
            print(f"Warning: Failed to log audit record: {e}")
    
    def cleanup_expired_sessions(self):
        """清理过期会话"""
        expired_sessions = []
        
        for session_id, session in self.active_sessions.items():
            if session.is_expired(self.session_timeout_minutes):
                expired_sessions.append(session_id)
        
        for session_id in expired_sessions:
            self._expire_session(session_id)
        
        return len(expired_sessions)
    
    def get_active_sessions_info(self) -> list:
        """获取活动会话信息"""
        return [session.get_session_info() for session in self.active_sessions.values()]
    
    def get_auth_statistics(self) -> Dict[str, Any]:
        """获取认证统计信息"""
        return {
            "active_sessions": len(self.active_sessions),
            "locked_users": len([k for k, v in self.failed_attempts.items() if len(v) >= self.max_login_attempts]),
            "session_timeout_minutes": self.session_timeout_minutes,
            "max_login_attempts": self.max_login_attempts,
            "lockout_duration_minutes": self.lockout_duration_minutes
        }
